#pragma once
#include <iostream>
#include <vector>
#include "Sem.hpp"
#include "Mutex.hpp"
static const int gcap = 5;

using namespace SemModule;
using namespace MutexModule;

template <class T>
class RingQueue
{
public:
    RingQueue(int cap = gcap)
        : _rq(cap),
          _cap(cap),
          _blank_sem(cap),
          _p_step(0),
          _data_sem(0),
          _c_step(0)
    {
    }
    void Equeue(const T &in)
    {
        // _pmutex.Lock();//在这里加锁？？
        //生产者
        //1、申请信号量，空位置的信号量
        _blank_sem.P();
        _pmutex.Lock();//还是在这里加，都可以，但是在这里加的话效率更高。因为所有的线程都可以去申请信号量，
                       //申请完毕再去竞争锁。反之除了申请到锁的线程，其他线程都要在锁里等待，它们都没有信号量
                       //下次再来的时候还要申请锁。就比如排队去电影院，是先每个人买票去排队，还是先排队再去一个一个买票
        //2、生产
        _rq[_p_step] = in;
        //3、更新下标
        _p_step++;
        _p_step%=_cap;

        _pmutex.Unlock();

        _data_sem.V();
    }
    void Pop(T *out)
    {
        //消费者
        //1、申请信号量，数据信号量
        _data_sem.P();
        _cmutex.Lock();

        //2、消费
        *out = _rq[_c_step];
        //3、更新下标
        ++_c_step;
        _c_step %= _cap;
        
        _cmutex.Unlock();

        _blank_sem.V();
    }
    ~RingQueue() {}

private:
    std::vector<T> _rq;
    int _cap;
    // 生产者
    Sem _blank_sem; // 空位置
    int _p_step;
    // 消费者
    Sem _data_sem; // 数据
    int _c_step;

    //用锁来维护多生产多消费
    Mutex _cmutex;
    Mutex _pmutex;
};
